
#include<vector>

#include <errno.h>
#include <sys/iomsg.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <string.h>
//#include <semaphore.h>
#include "../m_connection_tests/komunikacja1.2.h"
#include "iKomunikacja.h"

using namespace std;


static sem_t mutexAirportDB;
static sem_t mutexPlaneDB;
static sem_t mutexEventDB;
static sem_t mutexConns;

Connector* Connector::instance = NULL;

Connector * Connector::getInstance()
{
	if ( !instance ) {
		instance = new Connector();
	}
	return instance;
}

Connector::Connector()
{
	connName = new char[CONN_NAME_LENGTH];
	connName[0] = 'a';
	for( int i = 0; i < CONN_NAME_LENGTH; i++)
		connName[i] = '\0';
	strcpy(connName, "NO_NAME");
	
	sem_init( &mutexAirportDB, 0, 1);
	sem_init( &mutexPlaneDB, 0, 1);
	sem_init( &mutexEventDB, 0, 1);
	sem_init( &mutexConns, 0, 1);
	
	sem_init( &semAirportsDB, 0, 0);
	sem_init( &semPlanesDB, 0, 0);
	sem_init( &semEventsDB, 0, 0);
	
		connectionsOpened = new std::vector<ConnFD>();

}

void Connector::createListener( char * _connLocalName)
{
	//cout << "createListener" <<endl;

	/** zapisuje nazwe naszego kanalu**/

	strcpy(connName, _connLocalName);

	/** watek, ktory bedzie nasluchiwal**/	
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      
	pthread_create( NULL, &attr, &run , _connLocalName );
}




 int  Connector::isConnected( char * arg)
 {
 	int i;

 	for(i = 0; i < connectionsOpened->size() ; i++) {
			if(  strcmp(     (* connectionsOpened)[i].connName , arg)== 0) {
			//	cout << "takie same : "<< arg << " i : " <<(* connectionsOpened)[i].connName <<endl;
				return  (* connectionsOpened)[i].fd;
			}
	}
		
	return -1;
 }

ConnFD::ConnFD(  char * name, int _fd)
{
	fd=  _fd;
	connName = name;
}

void  Connector::addConn( char * arg, int fd)
{
	char * par = new char[30];
	strcpy(par, arg);
	
	connectionsOpened->push_back( *(new ConnFD( par, fd)));		
}
 
 void  Connector::closeAllConns( void )
{
	
	int i, j;
	vector <ConnFD>::iterator iter;
	
	sem_wait(&mutexConns);
	
	iter = connectionsOpened->begin();
	i = 	connectionsOpened->size();
	for( j = 0; j < i; j++) {
		name_close( (*iter).fd);
		connectionsOpened->erase(iter);
		iter = connectionsOpened->begin();
	}
//	cout << "closeAllConns . polaczen jest  : "<< 	connectionsOpened->size() << endl;
	
	sem_post(&mutexConns);
}

/*** Client Side of the code ***/
int sendMessage(  Message arg) {

    my_data_t * msg = &arg.data;
    int fd;
	
	sem_wait(&mutexConns);
	  
	if( (fd = Connector::getInstance()->isConnected(arg.getConnReceiverName()))==-1 ) {
    	if ((fd = name_open (arg.getConnReceiverName(), NAME_FLAG_ATTACH_GLOBAL)) == -1) {
    		cout <<"\nERROR sendMessage. pewnie nie ma " <<arg.getConnReceiverName() <<" albo gns -c zapomniales\n";
    	 	cout <<endl <<strerror(errno) << endl ;
    	 	sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
    	Connector::getInstance()->addConn(arg.getConnReceiverName(), fd);
    }

    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

     if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
  		cout <<"\nsendMessage ERROR1 destination\n";
    	cout <<endl <<strerror(errno) << endl ;
		sem_post(&mutexConns);
        return EXIT_FAILURE;
    }
    
    if( (fd = Connector::getInstance()->isConnected("GOOFY"))==-1 ) {
		if ((fd = name_open ( "GOOFY", NAME_FLAG_ATTACH_GLOBAL)) == -1) {
	  		cout <<"\nERROR sendMessage. pewnie nie ma goofy albo gns -c zapomniales\n";
	   		cout<< endl<<strerror(errno)<<endl;
    		sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
    	Connector::getInstance()->addConn("GOOFY", fd);
   	}
   
    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

  
    if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
    	cout <<endl<<"sendMessage ERROR2"<<endl;
      	cout<< endl<<strerror(errno)<<endl;
		sem_post(&mutexConns);
        return EXIT_FAILURE;
    }

	sem_post(&mutexConns);
    return EXIT_SUCCESS;
}



/**
	Funkcja zajmujaca sie komunikatem, ktory przyszedl przez siec
**/
void * messageReceived( void * arg)
{
	 lotnisko * lotnisko1;
	samolot * samolot1;
	zdarzenie * zdarz;
	char * nazwa;
	iKomunikacja * kom;
	Message * msg = ( Message *)arg;


	switch( msg->getType()) {

		case RES_GET_AIRPORTS_EV_DB :
			lotnisko1 = new lotnisko();
			lotnisko1->id_lotniska = msg->getIntArg1();
			lotnisko1->pojemnosc = msg->getIntArg2();
			lotnisko1->ilosc_szerokich_pasow = msg->getIntArg3();
			lotnisko1->ilosc_waskich_pasow = msg->getIntArg4();
			lotnisko1->aktywnosc = msg->getBoolArg1();
			lotnisko1->szerokosc_geo = msg->getFloatArg1();
			lotnisko1->dlugosc_geo = msg->getFloatArg2();
			nazwa = new char[CONN_NAME_LENGTH];
			strcpy( nazwa, msg->getStringArg1() );
			lotnisko1->nazwa = nazwa;
			Connector::getInstance()->nextAirportDB(lotnisko1);
			if( (msg->getNr()) == msg->getMessageLength())
				sem_post(&Connector::getInstance()->semAirportsDB);
			break;
			
		case RES_GET_PLANES_EV_DB :
			samolot1 = new samolot();
			nazwa = new char[CONN_NAME_LENGTH];
			strcpy( nazwa, msg->getStringArg1() );
			samolot1->nazwa = nazwa;
			samolot1->bak = msg->getFloatArg1();
		    samolot1->spalanie = msg->getFloatArg2();
			samolot1->predkosc = msg->getFloatArg3();
			samolot1->czas_obslugi = msg->getIntArg1();
			samolot1->czy_szeroki = msg->getBoolArg1();
			Connector::getInstance()->nextPlaneDB( samolot1);
			if( (msg->getNr()) == msg->getMessageLength())
				sem_post(&Connector::getInstance()->semPlanesDB);
			break;
			
		case RES_GET_EVENTS_EV_DB :
			zdarz = new zdarzenie();
			nazwa = new char[CONN_NAME_LENGTH];
			strcpy( nazwa, msg->getStringArg1() );
			zdarz->nazwa = nazwa;
			zdarz->typ = msg->getIntArg1();
		    zdarz->podtyp = msg->getIntArg2();
			Connector::getInstance()->nextEventDB( zdarz);
			if( (msg->getNr()) == msg->getMessageLength())
				sem_post(&Connector::getInstance()->semEventsDB);
			break;

		case MSG_KONCZE_SMIGAC :
			nazwa = new char[CONN_NAME_LENGTH];
			strcpy( nazwa, msg->getStringArg1() );
			Zdarzenia::getInstance().czy_wyladowal( atoi(msg->getConnSenderName()), nazwa );
			break;
			
		default:
			;
		}

}


void messageHandler( Message  * msg)
{
	//cout << "messageHandler" <<endl;

	/* watek, ktory bedzie nasluchiwal*/
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      		
	pthread_create( NULL, &attr, &messageReceived, msg );
}



/**
	Funkcja obslugujaca nasluchiwanie na zadania z sieci
**/
void* run( void*  arg) {

  name_attach_t *attach;

   Message msg;
    Message * msgProper;
   int rcvid;
	
   /* Create a local name (/dev/name/local/...) */
   if ((attach = name_attach(NULL, (char *)  arg, NAME_FLAG_ATTACH_GLOBAL)) == NULL) {
   	cout << "ERROR,  run: #"<< strerror(errno) << endl;
    return (void*)EXIT_FAILURE;
   }

   /* Do your MsgReceive's here now with the chid */
   while (1) {

       rcvid = MsgReceive(attach->chid, &(msg.data), sizeof(msg.data), NULL);

		
       if (rcvid == -1) {/* Error condition, exit */
           break;
       }

       if (rcvid == 0) {/* Pulse received */
           switch (msg.data.hdr.code) {
           case _PULSE_CODE_DISCONNECT:
               /*
                * A client disconnected all its connections (called
                * name_close() for each name_open() of our name) or
                * terminated
                */
               Connector::getInstance()->closeAllConns(); 
               ConnectDetach(msg.data.hdr.scoid);
               break;
           case _PULSE_CODE_UNBLOCK:
               /*
                * REPLY blocked client wants to unblock (was hit by
                * a signal or timed out).  It's up to you if you
                * reply now or later.
                */
               break;
           
           }
           continue;
       }


       /* name_open() sends a connect message, must EOK this */
       if (msg.data.hdr.type == _IO_CONNECT ) {
           MsgReply( rcvid, EOK, NULL, 0 );
           continue;
       }

       /* Some other QNX IO message was received; reject it */
       if (msg.data.hdr.type > _IO_BASE && msg.data.hdr.type <= _IO_MAX ) {
           MsgError( rcvid, ENOSYS );
           continue;
       }

       /* A message (presumable ours) received, handle */

     	MsgReply(rcvid, EOK, 0, 0);
  		msgProper = new Message();
		msg.copyMessage(msgProper);    
      messageHandler(msgProper);



   } 

   /* Remove the name from the space */
   name_detach(attach, 0);


   return (void*) 0;
}


void Connector::nextAirportDB( lotnisko * arg)
{

		sem_wait(&mutexAirportDB);
		airportsDB->push_back( *arg);
		sem_post(&mutexAirportDB);

}

void Connector::setListAirportsDB( std::vector<lotnisko> * arg)
{
		//cout << "Connector::setListAirportsDB: dodaje liste lotnisk"<<endl;
		airportsDB = arg;
}

void Connector::nextPlaneDB( samolot * arg)
{
		//cout << "Connector::nextPlaneDB: dodaje samolot do wektora"<<endl;
		sem_wait(&mutexPlaneDB);
		planesDB->push_back( *arg);
		sem_post(&mutexPlaneDB);
}

void Connector::setListPlanesDB( std::vector<samolot> * arg)
{
		//cout << "Connector::setListPlanesDB: dodaje liste samolotow"<<endl;	
		planesDB = arg;
}

void Connector::nextEventDB( zdarzenie * arg)
{
		//cout << "Connector::nextEventDB"<<endl;
		sem_wait(&mutexEventDB);
		eventsDB->push_back( *arg);
		sem_post(&mutexEventDB);
}

void Connector::setListEventsDB( std::vector<zdarzenie> * arg)
{
		//cout << "Connector::setListEventsDB"<<endl;
		eventsDB = arg;
}


